#include "msocket.h"

//////////////////////////////////////////////////////////////////////////
///
/// socket create/close
///
//////////////////////////////////////////////////////////////////////////
MSOCKET_API int socket_init(void)
{
#if defined(_WIN32)
    WORD wVersionRequested;
    WSADATA wsaData;

    wVersionRequested = MAKEWORD(2, 2);
    return WSAStartup(wVersionRequested, &wsaData);
#else
    return 0;
#endif
}

MSOCKET_API int socket_cleanup(void)
{
#if defined(_WIN32)
    return WSACleanup();
#else
    return 0;
#endif
}

MSOCKET_API int socket_geterror(void)
{
#if defined(_WIN32)
    return WSAGetLastError();
#else
    return errno;
#endif
}

MSOCKET_API socket_t socket_tcp(void)
{
#if defined(_WIN32)
    return socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#else
    return socket(PF_INET, SOCK_STREAM, 0);
#endif
}

MSOCKET_API socket_t socket_udp(void)
{
#if defined(_WIN32)
    return socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
#else
    return socket(PF_INET, SOCK_DGRAM, 0);
#endif
}

MSOCKET_API socket_t socket_raw(void)
{
#if defined(_WIN32)
    return socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
#else
    return socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
#endif
}

MSOCKET_API socket_t socket_rdm(void)
{
#if defined(_WIN32)
    return socket(AF_INET, SOCK_RDM, 0);
#else
    return socket(PF_INET, SOCK_RDM, 0);
#endif
}

MSOCKET_API int socket_close(socket_t sock)
{
#if defined(_WIN32)
    return closesocket(sock);
#else
    return close(sock);
#endif
}

//////////////////////////////////////////////////////////////////////////
///
/// socket operation
///
//////////////////////////////////////////////////////////////////////////
MSOCKET_API int socket_connect(IN socket_t sock, IN const struct sockaddr *addr, IN socklen_t addrlen)
{
    return connect(sock, addr, addrlen);
}

MSOCKET_API int socket_connect_ipv4(IN socket_t sock, IN const char *ip_or_dns, IN unsigned short port)
{
    int r;
    struct sockaddr_in addr;
    r = socket_addr_ipv4(&addr, ip_or_dns, port);
    if (r < 0)
        return r;
    return socket_connect(sock, (struct sockaddr *)&addr, sizeof(addr));
}

MSOCKET_API int socket_connect_ipv4_by_time(IN socket_t sock, IN const char *ip_or_dns, IN unsigned short port, IN int timeout)
{
    int r;
#if defined(OS_LINUX)
    int errcode = 0;
    int errlen = sizeof(errcode);
#endif
    r = socket_setnonblock(sock, 1);
    r = socket_connect_ipv4(sock, ip_or_dns, (unsigned short)port);
    assert(r <= 0);
#if defined(_WIN32)
    if (0 != r && WSAEWOULDBLOCK == WSAGetLastError())
#else
    if (0 != r && EINPROGRESS == errno)
#endif
    {
        // check timeout
        r = socket_select_write(sock, timeout);
        if (1 == r)
        {
#if defined(OS_LINUX)
            r = getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&errcode, (socklen_t *)&errlen);
            if (0 == r)
                r = -errcode;
#else
            r = 0;
#endif
        }
        else
        {
            r = -1;
        }
    }
    return r;
}

MSOCKET_API int socket_bind(IN socket_t sock, IN const struct sockaddr *addr, IN socklen_t addrlen)
{
    return bind(sock, addr, addrlen);
}

MSOCKET_API int socket_bind_any(IN socket_t sock, IN unsigned short port)
{
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    return socket_bind(sock, (struct sockaddr *)&addr, sizeof(addr));
}

MSOCKET_API int socket_listen(IN socket_t sock, IN int backlog)
{
    return listen(sock, backlog);
}

MSOCKET_API socket_t socket_accept(IN socket_t sock, OUT struct sockaddr *addr, INOUT socklen_t *addrlen)
{
    return accept(sock, addr, addrlen);
}

//////////////////////////////////////////////////////////////////////////
///
/// socket read/write
///
//////////////////////////////////////////////////////////////////////////
MSOCKET_API int socket_send(IN socket_t sock, IN const void *buf, IN size_t len, IN int flags)
{
#if defined(_WIN32)
    return send(sock, (const char *)buf, (int)len, flags);
#else
    return send(sock, buf, len, flags);
#endif
}

MSOCKET_API int socket_recv(IN socket_t sock, OUT void *buf, IN size_t len, IN int flags)
{
#if defined(_WIN32)
    return recv(sock, (char *)buf, (int)len, flags);
#else
    return recv(sock, buf, len, flags);
#endif
}

MSOCKET_API int socket_sendto(IN socket_t sock, IN const void *buf, IN size_t len, IN int flags, IN const struct sockaddr *to, IN socklen_t tolen)
{
#if defined(_WIN32)
    return sendto(sock, (const char *)buf, (int)len, flags, to, tolen);
#else
    return sendto(sock, buf, len, flags, to, tolen);
#endif
}

MSOCKET_API int socket_recvfrom(IN socket_t sock, OUT void *buf, IN size_t len, IN int flags, OUT struct sockaddr *from, OUT socklen_t *fromlen)
{
#if defined(_WIN32)
    return recvfrom(sock, (char *)buf, (int)len, flags, from, fromlen);
#else
    return recvfrom(sock, buf, len, flags, from, fromlen);
#endif
}

MSOCKET_API int socket_send_v(IN socket_t sock, IN const socket_bufvec_t *vec, IN size_t n, IN int flags)
{
#if defined(_WIN32)
    DWORD count;
    int r = WSASend(sock, (socket_bufvec_t *)vec, n, &count, flags, NULL, NULL);
    if (0 == r)
        return (int)count;
    return r;
#else
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = (struct iovec *)vec;
    msg.msg_iovlen = n;
    int r = sendmsg(sock, &msg, flags);
    return r;
#endif
}

MSOCKET_API int socket_recv_v(IN socket_t sock, IN socket_bufvec_t *vec, IN size_t n, IN int flags)
{
#if defined(_WIN32)
    DWORD count;
    int r = WSARecv(sock, vec, n, &count, (LPDWORD)&flags, NULL, NULL);
    if (0 == r)
        return (int)count;
    return r;
#else
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_iov = vec;
    msg.msg_iovlen = n;
    int r = recvmsg(sock, &msg, flags);
    return r;
#endif
}

MSOCKET_API int socket_sendto_v(IN socket_t sock, IN const socket_bufvec_t *vec, IN size_t n, IN int flags, IN const struct sockaddr *to, IN socklen_t tolen)
{
#if defined(_WIN32)
    DWORD count;
    int r = WSASendTo(sock, (socket_bufvec_t *)vec, n, &count, flags, to, tolen, NULL, NULL);
    if (0 == r)
        return (int)count;
    return r;
#else
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (struct sockaddr *)to;
    msg.msg_namelen = tolen;
    msg.msg_iov = (struct iovec *)vec;
    msg.msg_iovlen = n;
    int r = sendmsg(sock, &msg, flags);
    return r;
#endif
}

MSOCKET_API int socket_recvfrom_v(IN socket_t sock, IN socket_bufvec_t *vec, IN size_t n, IN int flags, IN struct sockaddr *from, IN socklen_t *fromlen)
{
#if defined(_WIN32)
    DWORD count;
    int r = WSARecvFrom(sock, vec, n, &count, (LPDWORD)&flags, from, fromlen, NULL, NULL);
    if (0 == r)
        return (int)count;
    return r;
#else
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));
    msg.msg_name = from;
    msg.msg_namelen = *fromlen;
    msg.msg_iov = vec;
    msg.msg_iovlen = n;
    int r = recvmsg(sock, &msg, flags);
    return r;
#endif
}

MSOCKET_API int socket_select(IN int n, IN fd_set *rfds, IN fd_set *wfds, IN fd_set *efds, IN struct timeval *timeout)
{
    return select(n, rfds, wfds, efds, timeout);
}

MSOCKET_API int socket_select_readfds(IN int n, IN fd_set *fds, IN struct timeval *timeout)
{
    return socket_select(n, fds, NULL, NULL, timeout);
}

MSOCKET_API int socket_select_writefds(IN int n, IN fd_set *fds, IN struct timeval *timeout)
{
    return socket_select(n, NULL, fds, NULL, timeout);
}

MSOCKET_API int socket_select_read(IN socket_t sock, IN int timeout)
{
#if defined(OS_LINUX)
    struct pollfd fds;
    fds.fd = sock;
    fds.events = POLLIN;
    fds.revents = 0;
    return poll(&fds, 1, timeout);
#else
    fd_set fds;
    struct timeval tv;
    assert(socket_invalid != sock); // linux: FD_SET error
    FD_ZERO(&fds);
    FD_SET(sock, &fds);

    tv.tv_sec = timeout / 1000;
    tv.tv_usec = (timeout % 1000) * 1000;
    return socket_select_readfds(sock + 1, &fds, timeout < 0 ? NULL : &tv);
#endif
}

MSOCKET_API int socket_select_write(IN socket_t sock, IN int timeout)
{
#if defined(OS_LINUX)
    struct pollfd fds;
    fds.fd = sock;
    fds.events = POLLOUT;
    fds.revents = 0;
    return poll(&fds, 1, timeout);
#else
    fd_set fds;
    struct timeval tv;

    assert(socket_invalid != sock); // linux: FD_SET error

    FD_ZERO(&fds);
    FD_SET(sock, &fds);

    tv.tv_sec = timeout / 1000;
    tv.tv_usec = (timeout % 1000) * 1000;
    return socket_select_writefds(sock + 1, &fds, timeout < 0 ? NULL : &tv);
#endif
}

MSOCKET_API int socket_readable(IN socket_t sock)
{
    return socket_select_read(sock, 0);
}

MSOCKET_API int socket_writeable(IN socket_t sock)
{
    return socket_select_write(sock, 0);
}

MSOCKET_API int socket_send_by_time(IN socket_t sock, IN const void *buf, IN size_t len, IN int flags, IN int timeout)
{
    int r;

    r = socket_select_write(sock, timeout);
    if (r <= 0)
#if defined(_WIN32)
        return 0 == r ? -WSAETIMEDOUT : r;
#else
        return 0 == r ? -ETIMEDOUT : r;
#endif

    r = socket_send(sock, buf, len, flags);
    return r;
}

MSOCKET_API int socket_send_all_by_time(IN socket_t sock, IN const void *buf, IN size_t len, IN int flags, IN int timeout)
{
    int r;
    size_t bytes = 0;

    while (bytes < len)
    {
        r = socket_send_by_time(sock, (const char *)buf + bytes, len - bytes, flags, timeout);
        if (r <= 0)
            return r; // <0-error

        bytes += r;
    }
    return bytes;
}

MSOCKET_API int socket_recv_by_time(IN socket_t sock, OUT void *buf, IN size_t len, IN int flags, IN int timeout)
{
    int r;

    r = socket_select_read(sock, timeout);
    if (r <= 0)
#if defined(_WIN32)
        return 0 == r ? -WSAETIMEDOUT : r;
#else
        return 0 == r ? -ETIMEDOUT : r;
#endif

    r = socket_recv(sock, buf, len, flags);
    return r;
}

MSOCKET_API int socket_recv_all_by_time(IN socket_t sock, OUT void *buf, IN size_t len, IN int flags, IN int timeout)
{
    int r;
    size_t bytes = 0;

    while (bytes < len)
    {
        r = socket_recv_by_time(sock, (char *)buf + bytes, len - bytes, flags, timeout);
        if (r <= 0)
            return r; // <0-error / 0-connection closed

        bytes += r;
    }
    return bytes;
}

//////////////////////////////////////////////////////////////////////////
///
/// socket options
///
//////////////////////////////////////////////////////////////////////////
static int socket_setopt_bool(IN socket_t sock, IN int optname, IN int enable)
{
#if defined(_WIN32)
    BOOL v = enable ? TRUE : FALSE;
    return setsockopt(sock, SOL_SOCKET, optname, (const char *)&v, sizeof(v));
#else
    return setsockopt(sock, SOL_SOCKET, optname, &enable, sizeof(enable));
#endif
}

static int socket_getopt_bool(IN socket_t sock, IN int optname, OUT int *enable)
{
    socklen_t len;
#if defined(_WIN32)
    int r;
    BOOL v;
    len = sizeof(v);
    r = getsockopt(sock, SOL_SOCKET, optname, (char *)&v, &len);
    if (0 == r)
        *enable = TRUE == v ? 1 : 0;
    return r;
#else
    len = sizeof(*enable);
    return getsockopt(sock, SOL_SOCKET, optname, enable, &len);
#endif
}

MSOCKET_API int socket_setkeepalive(IN socket_t sock, IN int enable)
{
    return socket_setopt_bool(sock, SO_KEEPALIVE, enable);
}

MSOCKET_API int socket_getkeepalive(IN socket_t sock, OUT int *enable)
{
    return socket_getopt_bool(sock, SO_KEEPALIVE, enable);
}

MSOCKET_API int socket_setlinger(IN socket_t sock, IN int onoff, IN int seconds)
{
    struct linger l;
    // l.l_onoff = (u_short)onoff;
    // l.l_linger = (u_short)seconds;
    l.l_onoff = onoff;
    l.l_linger = seconds;
    return setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char *)&l, sizeof(l));
}

MSOCKET_API int socket_getlinger(IN socket_t sock, OUT int *onoff, OUT int *seconds)
{
    int r;
    socklen_t len;
    struct linger l;

    len = sizeof(l);
    r = getsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&l, &len);
    if (0 == r)
    {
        *onoff = l.l_onoff;
        *seconds = l.l_linger;
    }
    return r;
}

MSOCKET_API int socket_setsendbuf(IN socket_t sock, IN size_t size)
{
    return setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (const char *)&size, sizeof(size));
}

MSOCKET_API int socket_getsendbuf(IN socket_t sock, OUT size_t *size)
{
    socklen_t len;
    len = sizeof(*size);
    return getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)size, &len);
}

MSOCKET_API int socket_setrecvbuf(IN socket_t sock, IN size_t size)
{
    return setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (const char *)&size, sizeof(size));
}

MSOCKET_API int socket_getrecvbuf(IN socket_t sock, OUT size_t *size)
{
    socklen_t len;
    len = sizeof(*size);
    return getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)size, &len);
}

MSOCKET_API int socket_setsendtimeout(IN socket_t sock, IN size_t seconds)
{
#if defined(_WIN32)
    return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&seconds, sizeof(seconds));
#else
    struct timeval tv;
    tv.tv_sec = seconds;
    tv.tv_usec = 0;
    return setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
#endif
}

MSOCKET_API int socket_getsendtimeout(IN socket_t sock, OUT size_t *seconds)
{
    socklen_t len;
#if defined(_WIN32)
    len = sizeof(*seconds);
    return getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)seconds, &len);
#else
    int r;
    struct timeval tv;
    len = sizeof(tv);
    r = getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, &len);
    if (0 == r)
        *seconds = tv.tv_sec;
    return r;
#endif
}

MSOCKET_API int socket_setrecvtimeout(IN socket_t sock, IN size_t *seconds)
{
#if defined(_WIN32)
    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&seconds, sizeof(seconds));
#else
    struct timeval tv;
    tv.tv_sec = *seconds;
    tv.tv_usec = 0;
    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
}

MSOCKET_API int socket_getrecvtimeout(IN socket_t sock, OUT size_t *seconds)
{
    socklen_t len;
#if defined(_WIN32)
    len = sizeof(*seconds);
    return getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)seconds, &len);
#else
    int r;
    struct timeval tv;
    len = sizeof(tv);
    r = getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, &len);
    if (0 == r)
        *seconds = tv.tv_sec;
    return r;
#endif
}

MSOCKET_API int socket_setreuseaddr(IN socket_t sock, IN int enable)
{
    return socket_setopt_bool(sock, SO_REUSEADDR, enable);
}

MSOCKET_API int socket_getreuseaddr(IN socket_t sock, OUT int *enable)
{
    return socket_getopt_bool(sock, SO_REUSEADDR, enable);
}

MSOCKET_API int socket_setnonblock(IN socket_t sock, IN int noblock)
{
    // 0-block, 1-no-block
#if defined(_WIN32)
    return ioctlsocket(sock, FIONBIO, (u_long *)&noblock);
#else
    // http://stackoverflow.com/questions/1150635/unix-nonblocking-i-o-o-nonblock-vs-fionbio
    // Prior to standardization there was ioctl(...FIONBIO...) and fcntl(...O_NDELAY...) ...
    // POSIX addressed this with the introduction of O_NONBLOCK.
    int flags = fcntl(sock, F_GETFL, 0);
    return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
    // return ioctl(sock, FIONBIO, &noblock);
#endif
}

MSOCKET_API int socket_setnondelay(IN socket_t sock, IN int nodelay)
{
    // 0-delay(enable the Nagle algorithm)
    // 1-no-delay(disable the Nagle algorithm)
    return setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&nodelay, sizeof(nodelay));
}

MSOCKET_API int socket_getunread(IN socket_t sock, OUT size_t *size)
{
#if defined(_WIN32)
    return ioctlsocket(sock, FIONREAD, (u_long *)size);
#else
    return ioctl(sock, FIONREAD, (int *)size);
#endif
}

MSOCKET_API int socket_getname(IN socket_t sock, OUT char *ip, OUT unsigned short *port)
{
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    if (socket_error == getsockname(sock, (struct sockaddr *)&addr, &addrlen))
        return socket_error;

    strcpy(ip, inet_ntoa(addr.sin_addr));
    *port = addr.sin_port;
    return 0;
}

MSOCKET_API int socket_getpeername(IN socket_t sock, OUT char *ip, OUT unsigned short *port)
{
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    if (socket_error == getpeername(sock, (struct sockaddr *)&addr, &addrlen))
        return socket_error;

    strcpy(ip, inet_ntoa(addr.sin_addr));
    *port = addr.sin_port;
    return 0;
}

MSOCKET_API int socket_isip(IN const char *ip)
{
    unsigned long addr;
    addr = inet_addr(ip);
    if (INADDR_NONE == addr && 0 != strcmp(ip, "255.255.255.255"))
        return -1;
    return 0;
}

MSOCKET_API int socket_ip(IN const char *ip_or_dns, OUT char *ip)
{
    struct hostent *host;
    if (0 == socket_isip(ip_or_dns))
    {
        strcpy(ip, ip_or_dns);
        return 0;
    }

    // MSDN: The gethostbyname function cannot resolve IP address strings passed to it.
    //		 Such a request is treated exactly as if an unknown host name were passed.
    host = gethostbyname(ip_or_dns);
    if (NULL == host || host->h_addrtype != AF_INET)
        return -1;

    assert(4 == host->h_length);
    // sprintf(ip, "%d.%d.%d.%d",
    //         (int)(unsigned char)(host->h_addr[0]), (int)(unsigned char)(host->h_addr[1]),
    //         (int)(unsigned char)(host->h_addr[2]), (int)(unsigned char)(host->h_addr[3]));

    //sprintf(ip, "%s.%s.%s.%s",
    //        (host->h_addr_list[0]), (host->h_addr_list[1]),
    //        (host->h_addr_list[2]), (host->h_addr_list[3]));
    // memcpy(ip, host->h_addr, host->h_length);
    if (inet_ntop(AF_INET, host->h_addr_list[0], ip, SOCKET_EXT_IPV4_LEN) == NULL)
{
		return -1;  // 转换失败
    }
    return 0;
}

MSOCKET_API int socket_addr_ipv4(OUT struct sockaddr_in *addrin, IN const char *ip_or_dns, IN unsigned short port)
{
    int r;
    char ip[16];
    r = socket_ip(ip_or_dns, ip);
    if (r < 0)
        return r;

    addrin->sin_family = AF_INET;
    addrin->sin_port = htons(port);
    addrin->sin_addr.s_addr = inet_addr(ip);
    return 0;
}

MSOCKET_API void socket_setbufvec(socket_bufvec_t *vec, int idx, void *ptr, size_t len)
{
#if defined(_WIN32)
    vec[idx].buf = (CHAR *)ptr;
    vec[idx].len = (ULONG)len;
#else
    vec[idx].iov_base = ptr;
    vec[idx].iov_len = len;
#endif
}
/*
MSOCKET_API int socket_multicast_join(IN socket_t sock, IN const char *group, IN const char *source, IN const char *local)
{
    struct ip_mreq_source imr;
    imr.imr_multiaddr.s_addr = (group && *group) ? inet_addr(group) : INADDR_ANY;
    imr.imr_sourceaddr.s_addr = (source && *source) ? inet_addr(source) : INADDR_ANY;
    imr.imr_interface.s_addr = (local && *local) ? inet_addr(local) : INADDR_ANY;
    return setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(imr));
}

MSOCKET_API int socket_multicast_leave(IN socket_t sock, IN const char *group, IN const char *source, IN const char *local)
{
    struct ip_mreq_source imr;
    imr.imr_multiaddr.s_addr = (group && *group) ? inet_addr(group) : INADDR_ANY;
    imr.imr_sourceaddr.s_addr = (source && *source) ? inet_addr(source) : INADDR_ANY;
    imr.imr_interface.s_addr = (local && *local) ? inet_addr(local) : INADDR_ANY;
    return setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&imr, sizeof(imr));
}

MSOCKET_API int socket_multicast_join_source(IN socket_t sock, IN const char *group, IN const char *source, IN const char *local)
{
    struct ip_mreq_source imr;
    imr.imr_multiaddr.s_addr = (group && *group) ? inet_addr(group) : INADDR_ANY;
    imr.imr_sourceaddr.s_addr = (source && *source) ? inet_addr(source) : INADDR_ANY;
    imr.imr_interface.s_addr = (local && *local) ? inet_addr(local) : INADDR_ANY;
    return setsockopt(sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(imr));
}

MSOCKET_API int socket_multicast_leave_source(IN socket_t sock, IN const char *group, IN const char *source, IN const char *local)
{
    struct ip_mreq_source imr;
    imr.imr_multiaddr.s_addr = (group && *group) ? inet_addr(group) : INADDR_ANY;
    imr.imr_sourceaddr.s_addr = (source && *source) ? inet_addr(source) : INADDR_ANY;
    imr.imr_interface.s_addr = (local && *local) ? inet_addr(local) : INADDR_ANY;
    return setsockopt(sock, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(imr));
}
*/
